﻿using lib.dmt.project.Extensions;
using lib.dmt.project.Helpers;
using lib.dmt.teltonika.server.Events;
using lib.dmt.teltonika.server.TCP;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NCrontab;
using NCrontab.Scheduler;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using ErrorEventArgs = lib.dmt.teltonika.server.Events.ErrorEventArgs;

namespace lib.dmt.teltonika.server.HostedServices
{
    public class TeltonikaTcpHostedService : IHostedService
    {
        #region default IHostedService fields
        private readonly IHostApplicationLifetime? _hostLifetime;
        private readonly IHostEnvironment? _hostingEnv;
        private readonly IConfiguration? _appConfig;
        private readonly IServiceProvider? _appServiceProvider;
        //private readonly ILogger<TeltonikaTcpHostedService>? _logger;
        //private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILoggerFactory? _loggerFactory;
        public TeltonikaTcpHostedService(
            //IHttpClientFactory httpClientFactory,
            IHostApplicationLifetime hostLifetime,
            IHostEnvironment hostingEnv,
            IConfiguration configuration,
            IServiceProvider serviceProvider,
            ILoggerFactory loggerFactory )
        {
            _hostLifetime = hostLifetime ?? throw new ArgumentNullException(nameof(hostLifetime));
            _hostingEnv = hostingEnv ?? throw new ArgumentNullException(nameof(hostingEnv));
            _appConfig = configuration ?? throw new ArgumentNullException(nameof(configuration));
            _appServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
            _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));

            //_logger = logger ?? throw new ArgumentNullException(nameof(logger));
            //_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));

            var localConfigFile = _appConfig.GetValue<string>("LocalConfigFiles:TeltonikaTcpHostedService");
            if (localConfigFile == null)
            {
                throw new Exception($"[TeltonikaTcpHostedService]; the application JSON config file missing the field: [LocalConfigFiles:TeltonikaTcpHostedService]");
            }
            localConfig = ConfigurationHelper.GetLocalConfiguration(localConfigFile);
            if (localConfig == null)
            {
                throw new NullReferenceException($"[TeltonikaTcpHostedService]; failed to create a localConfig instance based on the file:{localConfigFile}");
            }

            receiveJsonLogger = _loggerFactory.CreateLogger("ReceiveJsonLogger");
            receiveByteLogger = _loggerFactory.CreateLogger("ReceiveByteLogger");
            _logger = _loggerFactory.CreateLogger("AppLogger");
        }
        private readonly IConfiguration? localConfig;
        private readonly ILogger receiveJsonLogger;
        private readonly ILogger receiveByteLogger;
        private readonly ILogger _logger;

        #endregion
        public Task StartAsync(CancellationToken cancellationToken)
        {
            if (localConfig == null)
            {
                return Task.CompletedTask;
            }

            if (localConfig.IsFieldExists("ListenPort") == false ||
                localConfig.IsFieldExists("IdelTimeout") == false ||
                localConfig.IsFieldExists("StatisticPrint") == false)
            {
                throw new Exception($"[TeltonikaTcpHostedService]; missing the configruation fields, [ListenPort | IdelTimeout | StatisticPrint]");
            }
            _logger?.LogDebug($"[TeltonikaTcpHostedService]; LocalConfig; ListenPort={localConfig.GetValue<int>("ListenPort")}");
            _logger?.LogDebug($"[TeltonikaTcpHostedService]; LocalConfig; IdelTimeout={localConfig.GetValue<int>("IdelTimeout")}");
            _logger?.LogDebug($"[TeltonikaTcpHostedService]; LocalConfig; StatisticPrint={localConfig.GetValue<string>("StatisticPrint")}");

            Task.Run(async () =>
            {
                var server = new TcpServer(_loggerFactory, localConfig);
                server.Error += Server_Error;
                server.ClientDisconnected += Server_ClientDisconnected;
                server.ClientConnected += Server_ClientConnected;

                try
                {
                    //start the TCP server
                    server.Start(localConfig.GetValue<int>("ListenPort"));

                    IScheduler scheduler = new Scheduler();
                    scheduler.AddTask(
                        crontabSchedule: CrontabSchedule.Parse(localConfig.GetValue<string>("StatisticPrint")),
                        action: ct => 
                        {
                            var stats = server.ConnectionManager!.GetStats();
                            _logger?.LogTrace($"[Statistic]; {DateTime.Now.ToLocalString()}/Stats: {stats.ActiveConnections} active connections, " +
                                             $"{stats.TotalBytesReceived} bytes received, " +
                                             $"{stats.TotalBytesSent} bytes sent");
                        });

                    await scheduler.StartAsync(cancellationToken);

                    // Monitor connections every 10 seconds
                    //_ = Task.Run(async () =>
                    //{
                    //    while (true)
                    //    {
                    //        await Task.Delay(TimeSpan.FromMinutes(localConfig.GetValue<int>("StatisticPrint")));
                    //        var stats = server.ConnectionManager!.GetStats();
                    //        _logger?.LogTrace($"[Statisitc]; {DateTime.Now.ToLocalString()}/Stats: {stats.ActiveConnections} active connections, " +
                    //                         $"{stats.TotalBytesReceived} bytes received, " +
                    //                         $"{stats.TotalBytesSent} bytes sent");
                    //    }
                    //});

                    while (!cancellationToken.IsCancellationRequested)
                    {
                        await Task.Delay(1000);
                    }
                }
                finally
                {
                    server.Stop();
                }
            });

            return Task.CompletedTask;
        }

        private void Server_ClientConnected(object? sender, ClientConnectedEventArgs e)
        {
            _logger?.LogInformation($"[TeltonikaTcpHostedService]; CONNECTED: {e.Client.RemoteEndPoint}; ClientID:{e.Client.Id}");
        }

        private void Server_ClientDisconnected(object? sender, ClientDisconnectedEventArgs e)
        {
            _logger?.LogInformation($"[TeltonikaTcpHostedService]; DISCONNECTED: {e.Client.RemoteEndPoint}; ClientID:{e.Client.Id}; Reason:{e.Reason}");
        }

        private void Server_Error(object? sender, ErrorEventArgs e)
        {
            _logger?.LogError($"[TeltonikaTcpHostedService]; ERROR: {e.Exception.Message}");
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }
    }
}
